package br.unifor.g2cl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.SocketAddress;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

import net.sf.jgcs.Annotation;
import net.sf.jgcs.ControlSession;
import net.sf.jgcs.Message;
import net.sf.jgcs.Service;
import net.sf.jgcs.UnsupportedServiceException;

public class CompressDataSession extends DataSessionAdapter {

	public CompressDataSession(IMarshalDataSession data, ControlSession control) {
		super(data);
	}	
	
	private DataSessionWorker worker;
	
	public void setWorker(DataSessionWorker worker){
		this.worker = worker;
	}
	
	@Override
	public void multicast(Message msg, Service service, Object cookie,
			Annotation... annotation) throws IOException,
			UnsupportedServiceException {
		
		Message msgCompressed = createMessage();
		msgCompressed.setSenderAddress(msg.getSenderAddress());
		
		byte[] payload = msg.getPayload();		
		msgCompressed.setPayload(compressByteArray(payload));		
		super.multicast(msgCompressed, service, cookie, annotation);
		
	}
	
	@Override
	public void send(Message msg, Service service, Object cookie,
			SocketAddress destination, Annotation... annotation)
			throws IOException, UnsupportedServiceException {		
		byte[] payload = msg.getPayload();
		msg.setPayload(compressByteArray(payload));		
		super.send(msg, service, cookie, destination, annotation);
		
	}
	
	@Override
	public Object onMessage(Message msg) {

		byte[] payload = msg.getPayload();		
		msg.setPayload(decompressByteArray(payload));
		
		return super.onMessage(msg);
	}
	
	/*
	 *Original code retrieved from http://www.exampledepot.com/egs/java.util.zip/CompArray.html 
	 */
	private byte[] compressByteArray(byte[] input){
		if(worker != null){
			return  worker.doIt(input);
		}
		
		
	    // Create the compressor with highest level of compression
	    Deflater compressor = new Deflater();
	    compressor.setLevel(Deflater.BEST_COMPRESSION);
	    
	    // Give the compressor the data to compress
	    compressor.setInput(input);
	    compressor.finish();
	    
	    // Create an expandable byte array to hold the compressed data.
	    // You cannot use an array that's the same size as the orginal because
	    // there is no guarantee that the compressed data will be smaller than
	    // the uncompressed data.
	    ByteArrayOutputStream bos = new ByteArrayOutputStream(input.length);
	    
	    // Compress the data
	    byte[] buf = new byte[1024];
	    while (!compressor.finished()) {
	        int count = compressor.deflate(buf);
	        bos.write(buf, 0, count);
	    }
	    try {
	        bos.close();
	    } catch (IOException e) {
	    }
	    
	    // Get the compressed data
	    byte[] compressedData = bos.toByteArray();
	    
	    return compressedData;
	}
	
	/*
	 * Original code retrieved from http://www.exampledepot.com/egs/java.util.zip/DecompArray.html
	 */
	private byte[] decompressByteArray(byte[] compressedData){
	    
		if(worker != null){
			return  worker.undoIt(compressedData);
		}
		
	    // Create the decompressor and give it the data to compress
	    Inflater decompressor = new Inflater();
	    decompressor.setInput(compressedData);
	    
	    // Create an expandable byte array to hold the decompressed data
	    ByteArrayOutputStream bos = new ByteArrayOutputStream(compressedData.length);
	    
	    // Decompress the data
	    byte[] buf = new byte[1024];
	    while (!decompressor.finished()) {
	        try {
	            int count = decompressor.inflate(buf);
	            bos.write(buf, 0, count);
	        } catch (DataFormatException e) {
	        }
	    }
	    try {
	        bos.close();
	    } catch (IOException e) {
	    }
	    
	    // Get the decompressed data
	    byte[] decompressedData = bos.toByteArray();

	    return decompressedData;	
	}
}
